/**
* \file pico.y
* \brief gramática Yacc usada pelo parser para gerar a AST graças a ações semânticas. 
* \author Rafael Machado
* \author Ananias Tessaro
* \author Vinicius Schmitt
* \version 2
* \date 16/06/2011
*/

%{
  /* Aqui, pode-se inserir qualquer codigo C necessario ah compilacao
   * final do parser. Sera copiado tal como esta no inicio do y.tab.c
   * gerado por Yacc.
   */
  #include <stdio.h>
  #include <stdlib.h>
  #include "node.h"

	
char* lexema;


%}

%union {
  char* cadeia;
  struct _node * no;
}

/*Terminal*/
%token REAL
%token FOR
%token NEXT
%token REPEAT
%token UNTIL
%token CONST
%token CASE
%token INT
%token DOUBLE
%token CHAR
%token QUOTE
%token DQUOTE
%token LE
%token GE
%token EQ
%token NE
%token AND
%token OR
%token NOT
%token IF
%token THEN
%token ELSE
%token WHILE
%token END
%token TRUE
%token FALSE
%token<cadeia> INT_LIT
%token<cadeia> F_LIT
%token<cadeia> IDF

/*Não Terminal*/
%type<no> code 
%type<no> acoes
%type<no> comando
%type<no> enunciado
%type<no> expr
%type<no> lvalue
%type<no> declaracoes
%type<no> declaracao
%type<no> listadeclaracao
%type<no> tipo
%type<no> tipounico
%type<no> tipolista
%type<no> listadupla
%type<no> listaexpr
%type<no> chamaproc
%type<no> fiminstcontrole
%type<no> expbool

%left '+' '-'
%left '*' '/'
%left OR
%left AND
%left NOT


%start code

 /* A completar com seus tokens - compilar com 'yacc -d' */

%%
code: declaracoes acoes {       /*aloca*/
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
			        /*associa aos filhos os atributos dos simbolos à direita da produção*/
                                children[0]=$1;
			        children[1]=$2;
                                /*cria-se o nó correspondente ao não-terminal à esquerda da produção*/
				$$=create_node(0, program_node, "__code", NULL, 2, children);
			        syntax_tree = $$;
                           }
    | acoes { $$ = $1; syntax_tree = $$;  }
    ;

declaracoes: declaracao ';' 
			{ 
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=$1;
				children[1]=create_leaf(0, ';', ";", NULL);
				$$=create_node(0, decl_node, "__declaracoes", NULL, 2, children);
			}
           | declaracoes declaracao ';'
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=$1;
				children[1]=$2;
				children[2]=create_leaf(0, ';', ";", NULL);
				$$=create_node(0, decl_node, "__declaracoes", NULL, 3, children);
			}
           ;

declaracao: listadeclaracao ':' tipo {
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=$1;
				children[1]=create_leaf(0, ':', ":", NULL);;
				children[2]=$3;
				$$=create_node(0, decl_node, "__declaracao", NULL, 3, children);
			}
;

listadeclaracao: IDF  { $$ = create_leaf(0, idf_node, $1, NULL); } 
               | IDF ',' listadeclaracao  
			{
                                //Node**  children=malloc(sizeof(Node *)*MAX_CHILDREN_NUMBER);
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=create_leaf(0, idf_node, $1, NULL);
				children[1]=create_leaf(0, ',', ",", NULL);
				children[2]=$3;
				$$=create_node(0, decl_list_node, "__listadeclaracao", NULL, 3, children);
			}
               ;

tipo: tipounico { $$ = $1;}
    | tipolista { $$ = $1;}
    ;

tipounico: INT        {$$=create_leaf(0, int_kw_node, "int", NULL);}
         | DOUBLE     {$$=create_leaf(0, double_kw_node, "double", NULL);}
         | REAL      {$$=create_leaf(0, float_kw_node, "float", NULL);}
         | CHAR       {$$=create_leaf(0, char_kw_node, "char", NULL);}
         ;

tipolista: INT '[' listadupla ']' 
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=create_leaf(0, int_kw_node, "int", NULL);
				children[1]=create_leaf(0, '[', "[", NULL);
				children[2]=$3;
				children[3]=create_leaf(0, ']', "]", NULL);
				$$=create_node(0,tipo_list_node, "__tipolista", NULL, 4, children);
			}
         | DOUBLE '[' listadupla ']'
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=create_leaf(0, double_kw_node, "double", NULL);
				children[1]=create_leaf(0, '[', "[", NULL);
				children[2]=$3;
				children[3]=create_leaf(0, ']', "]", NULL);
				$$=create_node(0,tipo_list_node, "__tipolista", NULL, 4, children);
			}
         | REAL '[' listadupla ']'
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=create_leaf(0, float_kw_node, "real", NULL);
				children[1]=create_leaf(0, '[', "[", NULL);
				children[2]=$3;
				children[3]=create_leaf(0, ']', "]", NULL);
				$$=create_node(0,tipo_list_node, "__tipolista", NULL, 4, children);
			}
         | CHAR '[' listadupla ']'
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=create_leaf(0, char_kw_node, "char", NULL);
				children[1]=create_leaf(0, '[', "[", NULL);
				children[2]=$3;
				children[3]=create_leaf(0, ']', "]", NULL);
				$$=create_node(0,tipo_list_node, "__tipolista", NULL, 4, children);
			}
         ;

listadupla: INT_LIT ':' INT_LIT 
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=create_leaf(0, int_node, $1, NULL);
				children[1]=create_leaf(0, ':', ":", NULL);
                                children[2]=create_leaf(0, int_node, $3, NULL);
				$$=create_node(0,listdupla_node, "__listadupla", NULL, 3, children);
			}
          | INT_LIT ':' INT_LIT ',' listadupla
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=create_leaf(0, int_node, $1, NULL);
				children[1]=create_leaf(0, ':', ":", NULL);
                                children[2]=create_leaf(0, int_node, $3, NULL);
				children[3]=create_leaf(0, ',', ",", NULL);
				children[4]=$5;
				$$=create_node(0,listdupla_node, "__listadupla", NULL, 5, children);
			}
          ;

acoes: comando ';'  
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=$1;
				children[1]=create_leaf(0, ';', ";", NULL);
				$$=create_node(0, acoes_node, "__acoes", NULL, 2, children);
			}
    | comando ';' acoes
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=$1;
				children[1]=create_leaf(0, ';', ";", NULL);
				children[2]=$3;
				$$=create_node(0, acoes_node, "__acoes", NULL, 3, children);
			}
    ;

comando: lvalue '=' expr
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=$1;
				children[1]=create_leaf(0, '=', ":=", NULL);
				children[2]=$3;
				$$=create_node(0, lvalue_node, "__comando", NULL, 3, children);
			}
       | enunciado { $$ = $1;}
       ;

lvalue: IDF	{ $$ = create_leaf(0, idf_node, $1, NULL); } 
      | IDF '[' listaexpr ']' 
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=create_leaf(0, idf_node, $1, NULL);
				children[1]=create_leaf(0, '[', "[", NULL);
				children[2]=$3;
				children[3]=create_leaf(0, ']', "]", NULL);
				$$=create_node(0, lvalue_node, "__lvalue", NULL, 4, children);
			}
      ;

listaexpr: expr		{ $$=$1; }
	   | expr ',' listaexpr
			{
						Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
						children[0]=$1;
						children[1]=create_leaf(0, ',', ",", NULL);
						children[2]=$3;
						$$=create_node(0, listaexpr_node, "__listaexpr", NULL, 3, children);
			}
	   ;

expr: expr '+' expr  
			{
						Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
						children[0]=$1;
						children[1]=create_leaf(0, '+', "+", NULL);
						children[2]=$3;
						$$=create_node(0, plus_node, "__lvalue", NULL, 3, children);
			}
    | expr '-' expr
			{
						Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
						children[0]=$1;
						children[1]=create_leaf(0, '-', "-", NULL);
						children[2]=$3;
						$$=create_node(0, minus_node, "__lvalue", NULL, 3, children);
			}
    | expr '*' expr
			{
						Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
						children[0]=$1;
						children[1]=create_leaf(0, '*', "*", NULL);
						children[2]=$3;
						$$=create_node(0, mult_node, "__lvalue", NULL, 3, children);

			}
    | expr '/' expr
			{
						Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
						children[0]=$1;
						children[1]=create_leaf(0, '/', "/", NULL);
						children[2]=$3;
						$$=create_node(0, div_node, "__lvalue", NULL, 3, children);
			}
    | '(' expr ')'
			{ 
						Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
						children[0]=create_leaf(0, '(', "(", NULL);
						children[1]=$2;
						children[2]=create_leaf(0, ')', ")", NULL);
						$$=create_node(0, expr_node, "__lvalue", NULL, 3, children);
			}
    | INT_LIT  	{ $$ = create_leaf(0, int_node, $1, NULL); } 
    | F_LIT 	{ $$ = create_leaf(0, float_node, $1, NULL); } 
    | lvalue	{ $$ = $1; }
    | chamaproc { $$ = $1; } 
    ;

chamaproc: IDF '(' listaexpr ')'
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=create_leaf(0, idf_node, $1, NULL);
				children[1]=create_leaf(0, '(', "(", NULL);
				children[2]=$3;
				children[3]=create_leaf(0, ')', ")", NULL);
				$$=create_node(0, call_proc_node, "__chamaproc", NULL, 4, children);
			}
         ;

enunciado: expr { $$ = $1 ;}
         | IF '(' expbool ')' THEN acoes fiminstcontrole
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=create_leaf(0, if_kw_node, "if", NULL);
				children[1]=create_leaf(0, '(', "(", NULL);
				children[2]=$3;
				children[3]=create_leaf(0, ')', ")", NULL);

				children[4]=create_leaf(0, then_kw_node, "then", NULL);
				children[5]=$6;
				children[6]=$7;
				$$=create_node(0, if_node, "__enuciado", NULL, 7, children);
			}
         | WHILE '(' expbool ')' '{' acoes '}'
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=create_leaf(0, while_kw_node, "while", NULL);
				children[1]=create_leaf(0, '(', "(", NULL);
				children[2]=$3;
				children[3]=create_leaf(0, ')', ")", NULL);
				children[4]=create_leaf(0, '{', "{", NULL);
				children[5]=$6;
				children[6]=create_leaf(0, '}', "}", NULL);
				$$=create_node(0, while_node, "__enunciado", NULL, 7, children);
			}
         ;

fiminstcontrole: END		{ $$ = create_leaf(0, end_node, "end", NULL); }
               | ELSE acoes END	
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=create_leaf(0, else_node, "else", NULL);
				children[1]=$2;
				children[2]=create_leaf(0, end_node, "end", NULL);
				$$=create_node(0, fim_ctrl_node, "__fiminstcontrole", NULL, 3, children);
			}
               ;

expbool: TRUE 			{ $$ = create_leaf(0, true_node, "true", NULL); }
       | FALSE			{ $$ = create_leaf(0, false_node, "false", NULL); }
       | '(' expbool ')'	
			{ 
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=create_leaf(0, '(', "(", NULL);
				children[1]=$2;
				children[2]=create_leaf(0, '(', "(", NULL);
				$$=create_node(0, expbool_node, "__expbool", NULL, 3, children);
			}
       | expbool AND expbool
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=$1;
				children[1]=create_leaf(0, and_node, "and", NULL);
				children[2]=$3;
				$$=create_node(0, expbool_node, "__expbool", NULL, 3, children);
			}
       | expbool OR expbool
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=$1;
				children[1]=create_leaf(0, or_node, "or", NULL);
				children[2]=$3;
				$$=create_node(0, expbool_node, "__expbool", NULL, 3, children);
			}
       | NOT expbool
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=create_node(0, not_node, "NOT", NULL, 2, children);;
				children[1]=$2;
				$$=create_node(0, expbool_node, "__expbool", NULL, 2, children); 
			}
       | expr '>' expr
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=$1;
				children[1]=create_leaf(0, '>', ">", NULL);
				children[2]=$3;
				$$=create_node(0, expbool_node, "__expbool", NULL, 3, children);

			}
       | expr '<' expr
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=$1;
				children[1]=create_leaf(0, '<', "<", NULL);
				children[2]=$3;
				$$=create_node(0, expbool_node, "__expbool", NULL, 3, children);

			}
       | expr LE expr
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=$1;
				children[1]=create_leaf(0, inf_eq_node, "<=", NULL);
				children[2]=$3;
				$$=create_node(0, expbool_node, "__expbool", NULL, 3, children);
			}
       | expr GE expr
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=$1;
				children[1]=create_leaf(0, sup_eq_node, ">=", NULL);
				children[2]=$3;
				$$=create_node(0, expbool_node, "__expbool", NULL, 3, children);
			}
       | expr EQ expr
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=$1;
				children[1]=create_leaf(0, eq_node, "=", NULL);
				children[2]=$3;
				$$=create_node(0, expbool_node, "__expbool", NULL, 3, children);
			}
       | expr NE expr
			{
				Node**  children = (Node**) malloc(MAX_CHILDREN_NUMBER*sizeof(Node*));
				children[0]=$1;
				children[1]=create_leaf(0, neq_node, "!=", NULL);
				children[2]=$3;
				$$=create_node(0, expbool_node, "__expbool", NULL, 3, children);
			}
       ;
%%
 /* A partir daqui, insere-se qualqer codigo C necessario.
  */

char* progname;
int lineno;
extern FILE* yyin;

int main(int argc, char* argv[]) 
{
   if (argc != 2) {
     printf("uso: %s <input_file>. Try again!\n", argv[0]);
     exit(-1);
   }
   yyin = fopen(argv[1], "r");
   if (!yyin) {
     printf("Uso: %s <input_file>. Could not find %s. Try again!\n", 
         argv[0], argv[1]);
     exit(-1);
   }

   progname = argv[0];

   if (!yyparse()) 
   {
      printf("OKAY.\n"); 
      print_tree(syntax_tree,0); /* se o parser aceita a entrada então imprime a árvore a partir do nó raiz */
      deep_free_node(syntax_tree) ;
   }
   else 
      printf("ERROR.\n");

   return(0);
}

yyerror(char* s) {
  fprintf(stderr, "%s: %s", progname, s);
  fprintf(stderr, "line %d\n", lineno);
}
